﻿/*
Name: Tam_calendar.js
Description: The JavaScript scriptable calendar you can embed in any page.
Version: 1.0.0 beta
Author: Takayuki Miyoshi
Author URI: http://ideasilo.wordpress.com

Note:
    This script depends on Sam Stephenson's Prototype.js http://prototype.conio.net
    You need to load Prototype.js before this script, like this:
    <head>
        <script src="prototype.js" type="text/javascript"></script>
        <script src="tam_calendar.js" type="text/javascript"></script>
        ...
*/

/*  Copyright 2006  Takayuki Miyoshi

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/


var Calendar = function(elm) {
  
  this.container = $(elm);
  this.content = document.createElement('div');
  this.today = _parse_date(new Date());
  this.currentYear;
  this.currentMonth;
  this.weekStart = Week.SUNDAY;
  this.afterRenderHook;
  this.formatDate = function(date) { return date; }
  this.formatDay = function(day) { return Day.EN_SHORT[day]; }
  this.formatLabel = function() { return Month.EN_LONG[this.currentMonth] + " " + this.currentYear; }
  this.formatNav = function(pos) { return Nav.ARROW[pos]; }
  
  // Function: calender.render
  this.render = function(month, year) {
    this.currentYear = parseInt(year);
    this.currentMonth = parseInt(month);
    if (isNaN(this.currentYear)) this.currentYear = this.today.getFullYear();
    if (isNaN(this.currentMonth)) this.currentMonth = this.today.getMonth();
    var tmp_date = new Date(this.currentYear, this.currentMonth);
    this.currentYear = tmp_date.getFullYear();
    this.currentMonth = tmp_date.getMonth();
    
    this._clear_away();
    
    this.content = document.createElement('div');
    this.content.appendChild(this._render_label_and_arrows());
    this.content.appendChild(this._render_days_and_dates());
    this.container.appendChild(this.content);
    
    // Class handling
    Element.addClassName(this.content, 'calendar');
    Element.addClassName(this.content, 'y' + _make_up_digits(this.currentYear, 4));
    Element.addClassName(this.content, 'm' + _make_up_digits(this.currentMonth + 1, 2));
    Element.addClassName(this.content, 't-y' + _make_up_digits(this.today.getFullYear(), 4));
    Element.addClassName(this.content, 't-m' + _make_up_digits(this.today.getMonth() + 1, 2));
    Element.addClassName(this.content, 't-d' + _make_up_digits(this.today.getDate(), 2));
    Element.addClassName(this.content, 't-w' + this.today.getDay());
    
    // Calling after render hook
    if (this.afterRenderHook !== undefined)
      this.afterRenderHook.call(this);
  }
  
  // Function: calender.previousMonth
  this.previousMonth = function() {
    if (this.currentMonth != 0) {
      return { year: this.currentYear, month: this.currentMonth - 1 }
    } else {
      return { year: this.currentYear - 1, month: 11 }
    }
  }
  
  // Function: calender.nextMonth
  this.nextMonth = function() {
    if (this.currentMonth != 11) {
      return { year: this.currentYear, month: this.currentMonth + 1 }
    } else {
      return { year: this.currentYear + 1, month: 0 }
    }
  }
  
  this._render_days_and_dates = function() {
    var table = document.createElement('table');
    Element.addClassName(table, 'days-dates');
    var tbody = document.createElement('tbody');
    
    var tr = document.createElement('tr');
    for (var d = this.weekStart, td_count = 0; d < this.weekStart + 7; d++, td_count++) {
      var day = (d > 6) ? d - 7 : d;
      var td = document.createElement('td');
      
      // Class handling
      Element.addClassName(td, 'day');
      Element.addClassName(td, 'w' + day);
      if (Week.SUNDAY == day || Week.SATURDAY == day) {
	Element.addClassName(td, 'weekend');
      } else {
	Element.addClassName(td, 'weekday');
      }
      Element.addClassName(td, 'col' + td_count);
      
      td.innerHTML = '<a>' + this.formatDay(day) + '</a>';
      tr.appendChild(td);
    }
    tbody.appendChild(tr);
    
    var current_month_start = new Date(this.currentYear, this.currentMonth);
    var delta = current_month_start.getDay() - this.weekStart;
    if (delta < 0) delta += 7;
    var starting = new Date(current_month_start.valueOf() - delta * H24);
    
    var tr = document.createElement('tr');
    for (var d = starting, td_count = 0, tr_count = 0; 
	d.valueOf() < starting.valueOf() + 42 * H24; d = new Date(d.valueOf() + H24)) {
      var td = document.createElement('td');
      
      // Class handling
      Element.addClassName(td, 'date');
      Element.addClassName(td, 'y' + _make_up_digits(d.getFullYear(), 4));
      Element.addClassName(td, 'm' + _make_up_digits(d.getMonth() + 1, 2));
      Element.addClassName(td, 'd' + _make_up_digits(d.getDate(), 2));
      Element.addClassName(td, 'w' + d.getDay());
      if (this.today.valueOf() == d.valueOf()) Element.addClassName(td, 'today');
      if (Week.SUNDAY == d.getDay() || Week.SATURDAY == d.getDay()) {
	Element.addClassName(td, 'weekend');
      } else {
	Element.addClassName(td, 'weekday');
      }
      var previous = this.previousMonth();
      var next = this.nextMonth();
      if (d.getFullYear() == previous.year && d.getMonth() == previous.month) {
	Element.addClassName(td, 'previous-month');
      } else if (d.getFullYear() == this.currentYear && d.getMonth() == this.currentMonth) {
	Element.addClassName(td, 'current-month');
      } else if (d.getFullYear() == next.year && d.getMonth() == next.month) {
	Element.addClassName(td, 'next-month');
      }
      Element.addClassName(td, 'row' + tr_count);
      Element.addClassName(td, 'col' + td_count);
      
      td.innerHTML = '<a>' + this.formatDate(d.getDate()) + '</a>';
      tr.appendChild(td);
      td_count++;
      if (td_count > 6) {
	tbody.appendChild(tr);
	tr = document.createElement('tr');
	td_count = 0;
	tr_count++;
      }
    }
    
    table.appendChild(tbody);
    return table;
  }

  this._render_label_and_arrows = function() {
    var table = document.createElement('table');
    Element.addClassName(table, 'label-navs');
    var tbody = document.createElement('tbody');
    var tr = document.createElement('tr');
    
    var td = document.createElement('td');
    
    // Class handling
    Element.addClassName(td, 'nav');
    Element.addClassName(td, 'previous');
    
    td.innerHTML = '<a>' + this.formatNav(0) + '</a>';
    tr.appendChild(td);
    
    var td = document.createElement('td');
    
    // Class handling
    Element.addClassName(td, 'label');
    
    td.innerHTML = '<a>' + this.formatLabel() + '</a>';
    tr.appendChild(td);
    
    var td = document.createElement('td');
    
    // Class handling
    Element.addClassName(td, 'nav');
    Element.addClassName(td, 'next');
    
    td.innerHTML = '<a>' + this.formatNav(1) + '</a>';
    tr.appendChild(td);
    
    tbody.appendChild(tr);
    table.appendChild(tbody);
    return table;
  }
  
  this._clear_away = function() {
    while (this.container.hasChildNodes()) {
      this.container.removeChild(this.container.childNodes[0]);
    }
  }
}

var H24 = 86400000; // Twenty-four hours equal 86400000 milliseconds.

var Week = {
  SUNDAY: 0, MONDAY: 1, TUESDAY: 2, WEDNESDAY: 3, THURSDAY: 4, FRIDAY: 5, SATURDAY: 6 
}

var Day = {
  EN_LONG: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
  EN_SHORT: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
  EN_MINIMUM: ["S", "M", "T", "W", "T", "F", "S"],
  JA: ["日", "月", "火", "水", "木", "金", "土"]
}

var Month = {
  EN_LONG: ["January", "February", "March", "April", "May", "June", 
      "July", "August", "September", "October", "November", "December"],
  EN_SHORT: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
}

var Nav = {
  ARROW: ["&laquo;", "&raquo;"]
}

var _make_up_digits = function(number, digit) {
  number = number.toString();
  if (number.length < digit) {
    var short = digit - number.length;
    for (var i = 0; i < short; i++) {
      number = "0" + number;
    }
  }
  return number;
}

var _parse_date = function(date) {
  return new Date(date.getFullYear(), date.getMonth(), date.getDate());
}

// Utility functions.
Calendar.href = function(element, url) {
  element = $(element);
  var a = (element.tagName.match(/^a$/i)) ? element : element.getElementsByTagName('a')[0];
  a.href = url;
}

Calendar.dateToRFC3339 = function(date) {
  var year = _make_up_digits(date.getFullYear(), 4);
  var month = _make_up_digits(date.getMonth() + 1, 2);
  var date = _make_up_digits(date.getDate(), 2);
  return year + '-' + month + '-' + date;
}

// Extending original document DOM object.
document.getElementsByClassNames = function(classNames, parentElement) {
  var children = ($(parentElement) || document.body).getElementsByTagName('*');
  if (classNames.match(/^\./)) {
    classNames = classNames.slice(1).split('.');
    return $A(children).inject([], function(elements, child) {
      if (classNames.all(function(className) {
	  return child.className.match(new RegExp("(^|\\s)" + className + "(\\s|$)")); 
	})) elements.push(child);
      return elements;
    });
  } else {
    return $A(children).inject([], function(elements, child) {
      if (child.className.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
	elements.push(child);
      return elements;
    });
  }
}
